package tech.powerjob.worker.oss;


import lombok.extern.slf4j.Slf4j;
import lombok.val;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
import tech.powerjob.common.OmsConstant;
import tech.powerjob.worker.common.utils.SpringUtils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author WangNing
 * date  2022/3/29 17:11
 * @version 1.0
 */
@Slf4j
public class ExtClassLoader {


    private static final String LOCAL_PATH = "/usr/local/lib/libs";



    private static final Map<String, CustomClassLoader> classLoaderCache = new ConcurrentHashMap<>();
    private static final Object lock  = new Object();

    static class CustomClassLoader extends URLClassLoader {

        public CustomClassLoader(URL[] urls, ClassLoader parent) {
            super(urls, parent);
        }

        @Override
        public void addURL(URL url) {
            super.addURL(url);
        }

        public void addURLS(URL[] urls){
            for(URL url:urls){
                addURL(url);
            }
        }


    }

    public static ClassLoader getClassLoader(String jarName) {
        if(classLoaderCache.containsKey(jarName)){
            return classLoaderCache.get(jarName);
        }
        initClassLoader(jarName);
        return classLoaderCache.getOrDefault(jarName, null);
    }

    /**
     * 加载类
     * @param jarName jar的 oss 地址
     * @param className class类名
     * @return -
     * @throws ClassNotFoundException -
     */
    public static Class<?> loadClass(String jarName, String className) throws ClassNotFoundException{

        if(classLoaderCache.containsKey(jarName)){
            return classLoaderCache.get(jarName).loadClass(className);
        }else {
            synchronized (lock){
                if(classLoaderCache.containsKey(jarName)){
                    return classLoaderCache.get(jarName).loadClass(className);
                }else {
                    if(initClassLoader(jarName)!= null){
                        return classLoaderCache.get(jarName).loadClass(className);
                    }
                    return null;
                }
            }
        }
    }

    /**
     * 初始化 jarClassLoader
     * @return -
     */
    private static CustomClassLoader initClassLoader(String jarName){

        Path localPath = Paths.get(LOCAL_PATH, jarName);
        OutputStream outputStream = null;
        String httpPath = OmsConstant.OSS_HTTP_PATH + jarName;

        // 文件不存在从本地下载
        if(!Files.exists(localPath)){
            Request ossRequest = new Request.Builder().url(httpPath).build();


            // 从 oss 中下载
            InputStream jarInputStream = null;

            try {
                Response res = new OkHttpClient().newCall(ossRequest).execute();
                jarInputStream = res.body().byteStream();

                val result = localPath.toFile().createNewFile();
                outputStream = new FileOutputStream(localPath.toFile());
                IOUtils.copy(jarInputStream, outputStream);

            }catch (IOException e){
                log.error("无法从 oss 加载文件 {}", jarName);
                e.printStackTrace();
                return null;
            }finally {
                if(jarInputStream != null){
                    try{
                        jarInputStream.close();
                    }catch (IOException ignored){

                    }
                }
                if(outputStream!=null){
                    try {
                        outputStream.close();
                    } catch (IOException ignored) {

                    }
                }
            }
        }

        try {
            URL  url = localPath.toUri().toURL();
            log.info("create customClassloader from {}", jarName);
            final CustomClassLoader customClassLoader = new CustomClassLoader(new URL[]{url}, SpringUtils.getSpringClassLoader());
            classLoaderCache.put(jarName, customClassLoader);
            return customClassLoader;
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 删除某一任务jar
     * @param jarName -
     */
    public static void removeJar(String jarName){
        try{
            synchronized (lock){
                if(classLoaderCache.containsKey(jarName)){
                    classLoaderCache.remove(jarName).close();
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }




}
